package com.syg.boot.util.net;

import com.syg.boot.util.system.SystemPropertiesUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ServerSocketFactory;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Random;

/**
 * @author syg
 * @date 2021年11月24日19:43:12
 * @version 1.0
 */
public abstract class NetUtil {
    private static final Logger log = LoggerFactory.getLogger(NetUtil.class);
    public static final int PORT_RANGE_MIN = 1024;
    public static final int PORT_RANGE_MAX = 65535;
    private static final String IP_PATTERN = "(\\d{1,3}\\.){3}\\d{1,3}";
    private static final String COLON_SEPARATOR = ":";
    private static final Random RANDOW = new Random();
    private static InetAddress localAddress;
    private static String localHost;
    private static volatile String cachedLocalHostIp;

    public static InetAddress getLocalAddress() {
        return localAddress;
    }

    public static String getLocalHost() {
        return localHost;
    }

    /**
     * 校验端口是否可用
     * @param port
     * @return
     */
    public static boolean isPortAvailable(int port) {
        try {
            ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket(port, 1, InetAddress.getByName("localhost"));
            serverSocket.close();
            return true;
        } catch (Exception var2) {
            return false;
        }
    }

    public static int findRandomAvailablePort() {
        return findRandomAvailablePort(1024, 65535);
    }

    public static int findRandomAvailablePort(int minPort, int maxPort) {
        int portRange = maxPort - minPort;
        int searchCounter = 0;

        int candidatePort;
        do {
            ++searchCounter;
            if (searchCounter > portRange) {
                throw new IllegalStateException(String.format("Could not find an available tcp port in the range [%d, %d] after %d attempts", minPort, maxPort, searchCounter));
            }

            candidatePort = minPort + RANDOW.nextInt(portRange + 1);
        } while(!isPortAvailable(candidatePort));

        return candidatePort;
    }

    public static int findAvailablePortFrom(int minPort) {
        for(int port = minPort; port < 65535; ++port) {
            if (isPortAvailable(port)) {
                return port;
            }
        }

        throw new IllegalStateException(String.format("Could not find an available tcp port in the range [%d, %d]", minPort, 65535));
    }

    private static void initLocalAddress() {
        NetworkInterface nic = null;

        try {
            localAddress = InetAddress.getLocalHost();
            nic = NetworkInterface.getByInetAddress(localAddress);
        } catch (Exception var4) {
        }

        if (localAddress == null || nic == null || localAddress.isLoopbackAddress() || localAddress instanceof Inet6Address) {
            InetAddress lookedUpAddr = findLocalAddressViaNetworkInterface();

            try {
                localAddress = lookedUpAddr != null ? lookedUpAddr : InetAddress.getByName("127.0.0.1");
            } catch (UnknownHostException var3) {
            }
        }

        localHost = IpUtil.toString(localAddress);
    }

    private static InetAddress findLocalAddressViaNetworkInterface() {
        String preferNamePrefix = SystemPropertiesUtil.getString("localhost.prefer.nic.prefix", "LOCALHOST_PREFER_NIC_PREFIX", "bond0.");
        String defaultNicList = SystemPropertiesUtil.getString("localhost.default.nic.list", "LOCALHOST_DEFAULT_NIC_LIST", "bond0,eth0,em0,br0");
        InetAddress resultAddress = null;
        HashMap candidateInterfaces = new HashMap();

        try {
            Enumeration allInterfaces = NetworkInterface.getNetworkInterfaces();

            while(allInterfaces.hasMoreElements()) {
                NetworkInterface nic = (NetworkInterface)allInterfaces.nextElement();

                try {
                    if (!nic.isUp() || !nic.supportsMulticast()) {
                        continue;
                    }
                } catch (SocketException var9) {
                    continue;
                }

                String name = nic.getName();
                if (name.startsWith(preferNamePrefix)) {
                    resultAddress = findAvailableInetAddress(nic);
                    if (resultAddress != null) {
                        return resultAddress;
                    }
                } else {
                    candidateInterfaces.put(name, nic);
                }
            }

            String[] var11 = defaultNicList.split(",");
            int var12 = var11.length;

            for(int var13 = 0; var13 < var12; ++var13) {
                String nifName = var11[var13];
                log.debug("---------nifName=" + nifName);
                NetworkInterface nic = (NetworkInterface)candidateInterfaces.get(nifName);
                if (nic == null) {
                    log.debug("****nic****is***null");
                } else {
                    resultAddress = findAvailableInetAddress(nic);
                    if (resultAddress != null) {
                        return resultAddress;
                    }
                }
            }

            return null;
        } catch (SocketException var10) {
            return null;
        }
    }

    private static InetAddress findAvailableInetAddress(NetworkInterface nic) {
        if (nic == null) {
            log.debug("nic-------------is***null--------");
        }

        if (nic.getInetAddresses() == null) {
            log.debug("nic.getInetAddresses()-------------is***null--------");
        }

        Enumeration indetAddresses = nic.getInetAddresses();

        InetAddress inetAddress;
        do {
            if (!indetAddresses.hasMoreElements()) {
                return null;
            }

            inetAddress = (InetAddress)indetAddresses.nextElement();
        } while(inetAddress instanceof Inet6Address || inetAddress.isLoopbackAddress());

        return inetAddress;
    }

    public static void main(String[] args) {
        long t = System.currentTimeMillis();
        long l = t / 1000L;
        System.out.println(t);
        System.out.println(l);
        System.out.println(getHostIp());
    }

    public static String getLocalHostIp() {
        if (StringUtils.isNotEmpty(cachedLocalHostIp)) {
            return cachedLocalHostIp;
        } else {
            if (StringUtils.isEmpty(cachedLocalHostIp)) {
                Class var0 = NetUtil.class;
                synchronized(NetUtil.class) {
                    if (StringUtils.isEmpty(cachedLocalHostIp)) {
                        cachedLocalHostIp = getHostIp();
                    }
                }
            }

            return cachedLocalHostIp;
        }
    }

    private static String getHostIp() {
        String hostIp = null;
        InetAddress ip = null;

        try {
            Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces();

            while(netInterfaces.hasMoreElements()) {
                NetworkInterface ni = (NetworkInterface)netInterfaces.nextElement();
                Enumeration ips = ni.getInetAddresses();

                while(ips.hasMoreElements()) {
                    ip = (InetAddress)ips.nextElement();
                    if (isPublicIpAddress(ip)) {
                        return ip.getHostAddress();
                    }

                    if (isLocalIpAddress(ip)) {
                        hostIp = ip.getHostAddress();
                    }
                }
            }
        } catch (Exception var5) {
            var5.printStackTrace();
        }

        return hostIp;
    }

    private static boolean isPublicIpAddress(InetAddress ipAddress) {
        return !ipAddress.isSiteLocalAddress() && !ipAddress.isLoopbackAddress() && !isV6IpAddress(ipAddress);
    }

    private static boolean isLocalIpAddress(InetAddress ipAddress) {
        return ipAddress.isSiteLocalAddress() && !ipAddress.isLoopbackAddress() && !isV6IpAddress(ipAddress);
    }

    private static boolean isV6IpAddress(InetAddress ipAddress) {
        return ipAddress.getHostAddress().contains(":");
    }

    static {
        initLocalAddress();
    }
}
